{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib import pyplot as plt\n",
    "import seaborn as sns\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "import sys\n",
    "sys.path.append('../../../')\n",
    "\n",
    "from fedlab.contrib.dataset.adult import Adult\n",
    "\n",
    "from fedlab.utils.dataset import AdultPartitioner\n",
    "from fedlab.utils.functional import partition_report\n",
    "\n",
    "import torch\n",
    "from torch.utils.data import DataLoader"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Adult Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Source file already downloaded.\n",
      "Local file ../../../../data/adult-a9a/a9a loaded.\n",
      "Train data size: 32561\n",
      "Train feature number: 123\n",
      "Train class distribution: class0:class1 = 7841:24720\n"
     ]
    }
   ],
   "source": [
    "trainset = Adult('../../../../data/adult-a9a/', train=True, download=True)\n",
    "train_loader = DataLoader(trainset, batch_size=20, shuffle=True)\n",
    "print(f\"Train data size: {len(trainset)}\")\n",
    "print(f\"Train feature number: {trainset.data.shape[1]}\")\n",
    "print(f\"Train class distribution: class0:class1 = \"\n",
    "      f\"{int(trainset.targets.sum())}:{len(trainset)-trainset.targets.sum()}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Source file already downloaded.\n",
      "Local file ../../../../data/adult-a9a/a9a.t loaded.\n",
      "Test data size: 16281\n",
      "Test feature number: 123\n",
      "Test class distribution: class0:class1 = 3846:12435\n"
     ]
    }
   ],
   "source": [
    "testset = Adult('../../../../data/adult-a9a/', train=False, download=True)\n",
    "test_loader = DataLoader(trainset, batch_size=20, shuffle=True)\n",
    "print(f\"Test data size: {len(testset)}\")\n",
    "print(f\"Test feature number: {testset.data.shape[1]}\")\n",
    "print(f\"Test class distribution: class0:class1 = \"\n",
    "      f\"{int(testset.targets.sum())}:{len(testset)-testset.targets.sum()}\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Adult Data Partition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_clients = 10\n",
    "num_classes = 2\n",
    "seed = 2021\n",
    "\n",
    "col_names = [f\"class{i}\" for i in range(num_classes)]\n",
    "\n",
    "hist_color = '#4169E1'\n",
    "plt.rcParams['figure.facecolor'] = 'white'"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Label distribution skew\n",
    "### Quantity-based"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# perform partition\n",
    "noniid_major_label_part = AdultPartitioner(trainset.targets, \n",
    "                                           num_clients=num_clients,\n",
    "                                           partition=\"noniid-#label\", \n",
    "                                           major_classes_num=1,\n",
    "                                           seed=seed)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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xWIhOjqa3/zmN4M+LhERObMZhnHcsLzjjjt45ZVXSExMpKKigrVr1wKHZsnr1q1j9erVOJ1OfD4ft956K1OnTmX16tVkZGTw29/+lqioKLZs2eLf39atW4mMjAy4TotxrJj/M2+//TZFRUW0t7fT19fnP7hPPvkk4AHPRK7IMLyeUUNdhhxJdwgTMT2Xy4XX6+237b//+7+ZPHmy//XpXhC2adMmsrKyePfdd7nooov8l7VHjRrFAw88wMUXX8zmzZu58MILmT17NmPHjqWiooLW1lZiYmIASEpK4ve//z3nn38+l19+ORaLhXvvvZfo6GjuueceJk6cyJ/+9CfGjh1LSkoK//Zv/4bNZjuqlj8/F0ca1GrtO++8k6effprk5GTCwsIG0+W7JTIJirwnbiciIqZms9n42c9+xjXXXENYWBhJSUlER0f73//nf/5npk6dyve//30SEhLYu3cvAAsWLODjjz/GMAzS09NJTEyktLSUP/zhD1itVi677DIeffRRRowYwdKlS8nIyODAgQPk5eUdM5hPZFAz56lTpwb1gfZ3xbH++hMRkeMbzMx5ODvpmfMPfvADFixYQHZ2dr+V01OmTDk1FYqIiIjfoML58Kz5yL+ALBYLa9asCU1VIiIiw9igwrm+vj7UdYiIiMj/N6g7hO3cuZM777yTv/7rvwZg8+bN/O53vwtpYSIiIsPVoML5jjvuICMjg88++wyAiRMnHvc7yyIiIhK8QYXznj17uPnmmznrrEPNR4wYMTy/UiUiInIaDCqc/+Iv/oLPP//cf1eV9957j4iIiJAWJiIicjqc6kdG5uXlMXr06JO6PfSgFoQ99dRTXH/99bS2tnL11Veze/fugB9sISIicpSiUzzRM8HdA++44w7uuecebr/99qD3MaiZ85QpU2hoaOCdd97hN7/5DZs2bcLhcAQ9qIiIyFAJ5SMjAaZPn37UAzUCddyZ85o1a3C73bz88sv9tre0tACHHq01HGzc9tUpv/+riIjZneg+1WeiUD8y8lQ5bjg3NDTgdrv54x//eNR7Fotl2ISziIh8NwzmkZGPPPIInZ2dfPPNN2RkZAD/98jIm2++2Z99V155JcXFxWzdupXs7GxiY2NPWZ3Hvaz9+OOPA/D73//+qH/Lli077o537NhBTk4OMTExxMfHM3v2bFpaWmhra/N/SO71epk/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iYmJ2O12Vq5cCcATTzxBSkoKdrsdj8fjvwdxWloa9913H9OnT2fy5Mk0NjaSnZ1NbGwsjzzyCHDoYRyTJk0iNzcXh8PBD3/4Q/bt23fU2LW1tVx55ZVMmTKFm266iW+++eaoNmlpaTz44IOkpqYyceJE3nrrLQAqKiq45557/O0yMzP995geNWoUDz74IMnJycyYMYP169eTlpbG+PHjqampOaXnT0SOpnAWOUmvv/46kZGRfPDBBzQ3NzNr1iwA7rnnHhobG2lubmb//v28+uqr/j5nn302//mf/8ncuXO54YYb+NWvfkVzczMVFRV8/vnnAHz00Ud4PB4+/PBDzj//fP71X/+137h79uxh4cKFvPnmm7z//vu4XC6eeuqpY9bY19fH+vXreeaZZ3j88cdPeExdXV2kpaWxYcMGzjvvPB555BHq6uqoqqri0UcfDfZUicggKZxFTlJCQgJvvvkmDz74IG+99RYREREA1NfXM3XqVBISElizZg2bNm3y9zl8n/aEhARsNhtjxowhPDyc8ePH+x8uMG7cOK6++moAbrvtNv7rv/6r37jvvfcemzdv5uqrr8bpdLJ8+XLa29uPWePhB3skJyfT1tZ2wmM6++yz/X9kJCQkcM0112C1WklISBhUfxE5OXpkpMhJmjhxIhs2bOC1117joYce4tprryU/P59//Md/xOv1Mm7cOIqKiuju7vb3CQ8PB+Css87y/3z4dV9fH3D0Y/f+/LVhGMycOZMXX3zxhDUeHiMsLMy//xEjRnDw4EF/myPrs1qt/vGOrPHI+kQkdDRzFjlJn332Geeeey633XYbDzzwAO+//74/6C6++GK++eYbVq1aFfB+Ozo6ePfddwF48cUX+au/+qt+719xxRW8/fbb/O///i8A+/bto6WlZdD7j46OxufzcfDgQbZs2cL69esDrlFEQkMzZ5GTtHHjRhYsWMBZZ52F1Wrlueee44ILLuCuu+4iISGB6OhoUlJSAt7v5MmTWb58Of/wD/9AbGwsd999d7/3L7nkEioqKrjlllvo6ekBYOHChUycOHFQ+7/66qu5/PLLSUhIwG63M2XKlIBrFJHQ0FOpREyora2NzMxMmpubh7oUERkCuqwtIiJiMpo5i4iImIxmziIiIiajcBYRETEZhbOIiIjJKJxFRERMRuEsIiJiMv8PIEHcyK6AYUUAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/adult_noniid-label1_10clients.csv\"\n",
    "partition_report(trainset.targets, noniid_major_label_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "noniid_major_label_part_df = pd.read_csv(csv_file,header=1)\n",
    "noniid_major_label_part_df = noniid_major_label_part_df.set_index('client')\n",
    "for col in col_names:\n",
    "    noniid_major_label_part_df[col] = (noniid_major_label_part_df[col] * noniid_major_label_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "noniid_major_label_part_df[col_names].plot.barh(stacked=True)  \n",
    "# plt.tight_layout()\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/adult_noniid-label1_10clients.png\", \n",
    "            dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Distributed-based (Dirichlet) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# perform partition\n",
    "noniid_labeldir_part = AdultPartitioner(trainset.targets, \n",
    "                                        num_clients=num_clients,\n",
    "                                        partition=\"noniid-labeldir\", \n",
    "                                        dir_alpha=0.5,\n",
    "                                        seed=seed)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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u51LL72UF154YVC15ebm4vV6CQkJITo6mt/97neDnpeIiJzcDMM4aljedttt/OlPfyIxMZGysjLWr18PHFwlb9iwgbVr1+JwOPB6vdx8881MmTKFtWvXkp6ezu9//3uioqJob2/3na+jo4PIyEi/6wwxjhTzf+Gtt96ioKCA1tZW+vr6fJP75JNP/B7wZBQWEUvE3Cf97qc7hInISOZ0OvF4PP2O/dd//ReTJk3yvT7RG8I2b95MZmYm77zzDuedd57vsvZZZ53Ffffdx/nnn8+WLVs499xzmTlzJmPGjKGsrIzm5mZiYmIASEpK4tlnn+Wcc87hkksuISQkhLvvvpvo6GjuuusuJkyYwBtvvMGYMWNISUnhj3/8I1ar9bBa/vK9+K5B7da+/fbbeeKJJ0hOTiY0NHQwXU4pCWPC8QS0A1AhLCJiJlarlV/+8pdcddVVhIaGkpSURHR0tO/3//RP/8SUKVP4q7/6KxISEvj6668BWLhwIR9//DGGYZCWlkZiYiLFxcU8//zzWCwWLr74Yh5++GFGjRrFsmXLSE9PZ//+/eTk5BwxmI9lUCvnKVOmBPSB9qniSP/6ExGRoxvMynkkO+6V849//GMWLlxIVlZWv53Tl1566dBUKCIiIj6DCudDq+bv/gsoJCSEdevWBacqERGREWxQ4VxbWxvsOkREROT/DOoOYZ2dndx+++38zd/8DQBbtmzhD3/4Q1ALExERGakGFc633XYb6enpbNu2DYAJEyYc9TvLIiIiErhBhfOOHTu44YYbOO20g81HjRo1Ir9SJSIiciIMKpy/973vsXPnTt9dVd59913Cw/UwCBEROfkN9SMjc3JyuPDCC4/r9tCD2hD2+OOPM3v2bJqbm7niiiv44osv/H6whYiIyGEKhnihZ4I7MN52223cdddd3HrrrQGfY1Ar50svvZS6ujrefvttfve737F582bsdnvAg4qIiAyXYD4yEuDKK6887IEa/jrqynndunW4XC5eeumlfsebmpqAg4/WGgk2bd3j9/1fB7yv9pGY4F96IiIjQbAfGTlUjhrOdXV1uFwu/vznPx/2u5CQkBETziIicmoYzCMjH3roIXbv3s0333xDeno68P+PjLzhhht82Xf55ZdTWFhIR0cHWVlZxMbGDlmdR72s/atf/QqAZ5999rD/li9fftQTb9++nezsbGJi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UM736aefNn72s58ZhmEYK1euNG644YYTOb1TjsJ5AG+//bZx9dVX+14XFRUZRUVFw1hR8MyePduorq42JkyYYGzbts0wjIMBPmHCBMMwDp/71Vdfbbz99tvGtm3bjLi4ON/xP/7xj4bb7T6xxQeovb3dcLlcxhtvvOEL55E0/z179hjR0dHGgQMH+h0fKe9BR0eHERUVZezcudPo7e01Zs2aZbz22mun/Pw//fTTfuE8lPM91MYwDKO3t9c477zzDvvflwyeLmsPYOvWrYwdO9b3Oioqiq1btw5jRcHR0tJCQ0MDU6ZMobOzk4iICAAiIiL4/PPPgYHfi61btxIVFXXY8ZPB3XffzW9+8xtOO+3//y8wkub/ySefcMEFF/CTn/yEpKQkfvrTn9LV1TVi3oMxY8Zw33338cMf/pCIiAjCw8O5+uqrR8z8DxnK+X63z6hRowgPD2fnzp0naiqnHIXzAIwjfG4UEhIyDJUEzzfffMO1117Lk08+yTnnnDNgu4Hei5P1PXr55Ze58MILSU5OHlT7U23+AH19fbz//vvceeedNDQ08L3vfe+o+ypOtfdg165dVFZW8umnn7Jt2za6urp4/vnnB2x/qs3/WAKZ76n6XgwXhfMAoqKiaG9v973u6OggMjJyGCsaWr29vVx77bXMmTOHrKwsAC666CI+++wzAD777DMuvPBCYOD3Iioqio6OjsOOm91bb71FVVUV0dHRZGdns27dOm655ZYRM384OKeoqCimTJkCwHXXXcf7778/Yt6D119/nUsuuYQLLrgAi8VCVlYWb7/99oiZ/yFDOd/v9unr62PPnj384Ac/OFFTOeUonAeQkpLCxx9/zKeffsq3337LqlWrmD179nCXNSQMw+D2229n0qRJ/OIXv/Adnz17NitWrABgxYoVXHPNNb7jq1atoqenh08//ZSPP/6YyZMnExERwdlnn827776LYRg899xzvj5m9thjj9HR0UFLSwurVq3C5XLx/PPPj5j5A1x88cWMHTuWjz76CIA33niD+Pj4EfMe/PCHP+Tdd99l7969GIbBG2+8waRJk0bM/A8Zyvl+91xr1qzB5XJp5Xw8TvBn3CeVtWvXGrGxsca4ceOMRYsWDXc5Q+bNN980ACMhIcFITEw0EhMTjbVr1xo7duwwXC6XMX78eMPlchk7d+709Vm0aJExbtw4Y8KECf12o9bX1xtWq9UYN26c8fOf//yk2wBSW1vr2xA20ubf0NBgJCcnGwkJCcY111xjfPnllyPqPXj44YeNuLg4w2q1GrfccovR3d19Ss8/OzvbuPjii41Ro0YZY8aMMX7/+98P6Xz37dtnXHfddUZMTIyRkpJiNDc3n/A5nkp0+04RERGT0WVtERERk1E4i4iImIzCWURExGQUziIiIiajcBYRETEZhbPISSY1NRWPxzPcZYhIECmcRURETEbhLHKcurq6mDVrFomJidhsNlavXg3Ao48+SkpKCjabDbfb7bv3cGpqKvfccw9XXnklkyZNor6+nqysLGJjY3nooYeAgw8kmThxInPnzsVut3Pdddexd+/ew8aurq7m8ssv59JLL+X666/nm2++OaxNamoq999/P5MnT2bChAm8+eabAJSVlXHXXXf52mVkZLB+/XoAzjrrLO6//36Sk5OZNm0a7733HqmpqYwbN46qqqohff9E5HAKZ5Hj9OqrrxIZGckHH3xAY2MjM2bMAOCuu+6ivr6exsZG9u3bx8svv+zrc/rpp/Mf//EfzJs3j2uuuYann36axsZGysrKfE/y+eijj3C73Xz44Yecc845/Mu//Eu/cXfs2MGiRYt4/fXXef/993E6nTz++ONHrLGvr4/33nuPJ598kl/96lfHnFNXVxepqals3LiRs88+m4ceeoiamhoqKip4+OGHA32rRGSQFM4ixykhIYHXX3+d+++/nzfffJPw8HAAamtrmTJlCgkJCaxbt47Nmzf7+hy6T3tCQgJWq5WIiAjCwsIYN26c7+EBY8eO5YorrgDglltu4T//8z/7jfvuu++yZcsWrrjiChwOBytWrKC1tfWINR56uElycjItLS3HnNPpp5/u+0dGQkICV111FRaLhYSEhEH1F5HjM2q4CxA52U2YMIGNGzfyyiuv8MADD3D11VeTm5vLP/zDP+DxeBg7diwFBQV0d3f7+oSFhQFw2mmn+X4+9Lqvrw84/HF7f/naMAymT5/OypUrj1njoTFCQ0N95x81ahQHDhzwtflufRaLxTfed2v8bn0iEjxaOYscp23btnHmmWdyyy23cN999/H+++/7gu7888/nm2++Yc2aNX6ft62tjXfeeQeAlStX8td//df9fn/ZZZfx1ltv8T//8z8A7N27l6ampkGfPzo6Gq/Xy4EDB2hvb+e9997zu0YRCQ6tnEWO06ZNm1i4cCGnnXYaFouFZ555hu9///vccccdJCQkEB0dTUpKit/nnTRpEitWrOBnP/sZsbGx3Hnnnf1+f8EFF1BWVsZNN91ET08PAIsWLWLChAmDOv8VV1zBJZdcQkJCAjabjUsvvdTvGkUkOPRUKhETamlpISMjg8bGxuEuRUSGgS5ri4iImIxWziIiIiajlbOIiIjJKJxFRERMRuEsIiJiMgpnERERk1E4i4iImMz/AijVqMSNJGsDAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/adult_noniid_labeldir_10clients.csv\"\n",
    "partition_report(trainset.targets, noniid_labeldir_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "noniid_labeldir_part_df = pd.read_csv(csv_file,header=1)\n",
    "noniid_labeldir_part_df = noniid_labeldir_part_df.set_index('client')\n",
    "for col in col_names:\n",
    "    noniid_labeldir_part_df[col] = (noniid_labeldir_part_df[col] * noniid_labeldir_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "noniid_labeldir_part_df[col_names].plot.barh(stacked=True)  \n",
    "# plt.tight_layout()\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/adult_noniid_labeldir_10clients.png\", \n",
    "            dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quantity skew (Dirichlet)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# perform partition\n",
    "unbalance_part = AdultPartitioner(trainset.targets, \n",
    "                                  num_clients=num_clients,\n",
    "                                  partition=\"unbalance\", \n",
    "                                  dir_alpha=0.5,\n",
    "                                  seed=seed)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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5/PR2dnJlClT/O1cLhculyvocUpKSnjggQcC6lNfX091dTVvvvkm4eHhvPfee0GPLyIiEgohmTnX19djtVpZvny5f5vT6WTu3LmD2m3evJnMzEwAuru7yc3NJTU1leTkZKqrqwGoqKggOzubhQsXEhcX55/pFhQU0NPTg9PpDOh+zk8++SQFBQWEh4cDcNVVV43ouYqIiIy2kIRzc3MzKSkpAfUpLi7G7XbT2NhIfX09K1eupLu7Gzg+696wYQPbt29nw4YN7Nmzh9LSUi666CJ8Ph9PP/30sMdpaWnh1VdfZc6cOdxwww00Njaetl1ZWZl/Zn/giG55LSIiYyckp7WDUVtbS01NDY8++igAvb29dHR0AJCenk5ERAQACQkJtLe3Dzo9HoiBgQE+/PBDXn/9dRobG7n11lt5++23sVgsg9p5PB48Hg8ArqiwYJ+WiIhIwEISzjabjY0bNwbUxzAMKisriY+PH7R9y5Yt/lPQAGFhYQwMDARdW3R0NNnZ2VgsFmbPns0FF1zAwYMHufLKK4Pep4iIyGgKyWltt9tNX18f5eXl/m2NjY00NDQM2ScjI4PVq1djGMdPITc1nf2zxVarlf7+/oBq+9u//VteeeUV4Pgp7k8++YQrrrgioH2IiIiEUkjC2WKxUFVVRV1dHbGxsdhsNoqKioiKihqyT2FhIf39/TgcDux2O4WFhWcdx+Px4HA4AloQlpuby9tvv43dbicnJ4d169adckpbRERkPFmME1NVGZIrKgzvO0fHuwwRkXOKy+XC6/WOdxnnJF0hbDiikse7AhEROY8onEVERExG4SwiImIyCmcRERGTUTiLiIiYjMJZRETEZBTOIiIiJqNwFhERMRmFs4iIiMkonEVERExG4SwiImIyprmfs5lt33eImIJN412GiIxQ28Ql413CuaXo0Kjvsr+/n71799Lb2zvq+z6XTJw4kejoaKxW62m/r3AWEZExs3fvXi655BJiYmLO2zsCGobB+++/z969e7nmmmtO2yZkp7X3799PTk4OsbGxJCQksGjRIlpaWmhra8NutwPg9XrJy8sLeoySkpKA+xQVFTF58mScTidOp5Pnn38+6PFFRCQwvb29XH755edtMMPx2ypffvnlZzx7EJJwNgyDrKws0tLSaG1tZefOnZSUlNDZ2TmoncvlYtWqVUGPE0w4A9x33334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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/adult_unbalance_10clients.csv\"\n",
    "partition_report(trainset.targets, unbalance_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "unbalance_part_df = pd.read_csv(csv_file,header=1)\n",
    "unbalance_part_df = unbalance_part_df.set_index('client')\n",
    "for col in col_names:\n",
    "    unbalance_part_df[col] = (unbalance_part_df[col] * unbalance_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "unbalance_part_df[col_names].plot.barh(stacked=True)  \n",
    "# plt.tight_layout()\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/adult_unbalance_10clients.png\", \n",
    "            dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## IID"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# perform partition\n",
    "iid_part = AdultPartitioner(trainset.targets, \n",
    "                            num_clients=num_clients,\n",
    "                            partition=\"iid\",\n",
    "                            seed=seed)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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p6CgIOTxRUREwiEsl7UNwyA7O5u8vDyefvppAPx+P11dXUyaNCnQzu1243a7Qx6ntLSUe+65J6g+DQ0N1NbW8s4772Cz2fj4449DHl9ERCQcwrJybmhowGq1snjx4sAxl8vF7NmzB7TbuHEjWVlZAHR3d5Ofn09qaiozZsygtrYWgKqqKnJycpg/fz7x8fGBlW5hYSEHDhzA5XIF9Tznxx9/nMLCQmw2GwATJkwY1lxFRERGWljCuaWlhZSUlKD6lJSU4PF4aGpqoqGhgWXLltHd3Q0cXnWvXbuWrVu3snbtWnbs2EFZWRmnnXYafr+fJ598csjjtLa28uqrrzJr1iwuu+wympqajtmuoqIisLI/uF+bIkRE5OQxzW7turo61q9fz4oVKwDo6emhs7MTgIyMDCIjIwFITEyko6NjwOXxYPT39/P555/z5ptv0tTUxDXXXMP777+PxWIZ0M7r9eL1egGwRcWHOi0REZGghSWc7XY769atC6qPYRhUV1eTkJAw4PimTZsCl6ABIiIi6O/vD7m2mJgYcnJysFgspKWlccopp7Bnzx7OP//8kM8pIiIyksJyWdvj8dDb20tlZWXgWFNTE42NjYP2yczMZNWqVRiGAUBzc/MJx7FarfT19QVV29/8zd/wyiuvAIcvcX/99decd955QZ1DREQknMISzhaLhZqaGurr64mLi8Nut1NcXEx0dPSgfYqKiujr68PpdOJwOCgqKjrhOF6vF6fTGdSGsPz8fN5//30cDge5ubmsWbPmqEvaIiIio8liHFmqyqBsUfFE5T0y2mWISIh0h7AQDfMOYW63G5/PN0LFfLeYZkOYmSVNjMRXdsVolyEiIdMnLmRs0e07RURETEbhLCIiYjIKZxEREZNROIuIiJiMwllERMRkFM4iIiImo3AWERExGYWziIiIySicRURETEbhLCIiYjK6fecQbN31BbGFG0a7DBEJke6tHaJh3lv7WPr6+ti5cyc9PT0jfu6xZPz48cTExGC1Wo/5c4WziIicNDt37uTMM88kNjb2O/tEQMMw+PTTT9m5cycXXXTRMduE7bL27t27yc3NJS4ujsTERBYsWEBrayvt7e04HA4AfD4fS5cuDXmM0tLSoPsUFxczceJEXC4XLpeLF154IeTxRUQkOD09PZx77rnf2WCGw49VPvfcc4979SAs4WwYBtnZ2aSnp9PW1sb27dspLS2lq6trQDu3283KlStDHieUcAa4/fbb8fv9+P1+FixYEPL4IiISvO9yMB9xor+DsIRzQ0MDVquVxYsXB465XC5mz549oN3GjRvJysoCoLu7m/z8fFJTU5kxYwa1tbUAVFVVkZOTw/z584mPj6egoACAwsJCDhw4gMvlYtGiReGYhoiIfAcUFxezYsWKETvfiy++SEJCAlOmTKGsrCykcwzpPednnnmGq6+++oTHjmhpaSElJSWoQkpKSvB4PKxevZq9e/eSlpbG3LlzAfD7/TQ3N2Oz2UhISGDJkiWUlZVRXl6O3+8PahyA8vJynnjiCdxuN//6r//KOeecc1SbiooKKioqADi4X8+CFREJh5HebNtedsWIni9YBw8e5Kc//Sn19fXExMSQmprKwoULSUxMDOo8Q1o5P/jgg0M6Nhx1dXWUlZXhcrlIT0+np6eHzs5OADIyMoiMjGT8+PEkJibS0dER8ji33HILbW1t+P1+oqKiuOOOO47Zzuv14vP58Pl8RJweGfJ4IiJiLk888QROp5Pk5GT+9m//dsDPKisrSU1NJTk5mauuuor9+/cDhxekDoeD5ORk5syZA8C2bdtIS0vD5XLhdDp577332Lx5M1OmTGHy5Mmceuqp5ObmBq4EB+O4K+c//OEPvPDCC+zatWvAxq0vv/ySceMG72q321m3bl1QhRiGQXV1NQkJCQOOb9q0CZvNFvg+IiKC/v7+oM79TRdccEHg65tvvjlwWV1ERL79tm3bRklJCa+99hrnnXcen3322YC9Tzk5Odx8880A3Hvvvfz2t79lyZIlPPDAA7z00ktMnDiRvXv3AvDv//7v/NM//ROLFi3i66+/5uDBg7z99ttMmjQpcL6YmBg2bdoUdJ3HXTlHR0fjdrsZP348KSkpgT8LFy7kpZdeGrSfx+Oht7eXysrKwLGmpiYaGxsH7ZOZmcmqVaswDAOA5ubmExZvtVrp6+s7Ybtv+uijjwJf19TUBHaOi4jIt98rr7zCj3/8Y8477zwAvve97w34eUtLC7NnzyYpKYknn3ySbdu2AXDppZdy4403UllZycGDBwG45JJLKC0t5Ze//CUdHR2cdtppgQz7plA2wB135ZycnExycjLXXXfdoB+UPhaLxUJNTQ233XYbZWVljB8/ntjYWB555JFB+xQVFXHbbbfhdDoxDIPY2Fief/75447j9XpxOp3MnDmTJ598cki1FRQU4Pf7sVgsxMbG8utf/3rI8xIRkbHNMIzjhuWNN97Ic889R3JyMlVVVWzcuBE4vEretGkTGzZswOVy4ff7ue6665g1axYbNmwgMzOT3/zmN8TExLBjx47A+Xbu3El0dHTQdVqMY8X8n3nttdcoLi6mo6OD/v7+wOTef//9oAcci2xR8UTlPTLaZYhIiHSHsBAN8w5hbrcbn8834Nj//M//MH369MD3J3tD2LZt28jOzuaNN97g3HPPDVzWPuOMM7jzzjs577zz2L59O+eccw4LFixg4sSJVFVV0dbWRlxcHAAzZszgd7/7HWeddRYXXXQRFouF2267jdjYWG699VamTp3KH//4RyZOnEhqair/+Z//id1uP6qWP/+7+KYh7da+6aabePjhh0lJSSEiImIoXb5VkiZG4hvlHYAiMhz6xIUcZrfb+fnPf85ll11GREQEM2bMIDY2NvDzf/7nf2bWrFl8//vfJykpiX379gGwbNky3nvvPQzDICMjg+TkZMrKyviP//gPrFYrF154Ib/4xS8YN24c5eXlZGZmcvDgQfLz848ZzCcypJXzrFmzQnpD+9viWP/6ExGR4xvKyvm7bNgr5x/+8IcsW7aMnJycATunZ86cOTIVioiISMCQwvnIqvmb/wKyWCy88sor4alKRETkO2xI4dzQ0BDuOkREROT/N6Q7hHV1dXHTTTfx13/91wBs376d3/72t2EtTERE5LtqSOF84403kpmZyYcffgjA1KlTj/uZZREREQndkMJ5z549XHPNNZxyyuHm48aN+05+pEpERORkGFI4/8Vf/AWffvpp4K4qb775JpGRehiEiIiMfSP9yMj8/HwmTJgwrNtDD2lD2EMPPcTChQtpa2vj0ksv5ZNPPgn6wRYiIiJHKR7hhd4w72o2Em688UZuvfVWbrjhhpDPMaSV88yZM2lsbOT111/n17/+Ndu2bcPpdIY8qIiIyGgJ5yMjAebMmXPUAzWCddyV8yuvvILH4+HZZ58dcLy1tRU4/Git74Ktu74Y8fu/isjJo3trh8gEq9CRFu5HRo6U44ZzY2MjHo+H3//+90f9zGKxfGfCWUREvh2G8sjIe++9l7179/LVV1+RmZkJ/L9HRl5zzTWB7LvkkksoKSlh586d5OTkEB8fP2J1Hvey9v333w/A7373u6P+rF69+rgn3r17N7m5ucTFxZGYmMiCBQtobW2lvb098Ca5z+dj6dKlIRdfWloact8VK1ZgsVjYs2dPyOcQEZGxZSiPjCwvL2fr1q3cd9999PT0AIdXycuXL2fHjh24XC4+/fRTrrvuOtavX89pp51GZmbmiN4187gr54ceeui4nX/2s58d87hhGGRnZ5OXl8fTTz8NgN/vp6uri0mTJgXaud1u3G53sDUHlJaWcs899wTdb8eOHdTX1/OXf/mXIY8tIiJjT0ZGBtnZ2dx+++2BR0Z+0759+4iKiqKvr48nn3ySiRMnAtDW1sasWbOYNWsWv//979mxYwdffPEFkydPZunSpbz//vu88847eDyeEanzuCvnffv2Dfrnq6++GrRfQ0MDVquVxYsXB465XC5mz549oN3GjRvJysoCoLu7m/z8fFJTU5kxYwa1tbUAVFVVkZOTw/z584mPj6egoACAwsJCDhw4gMvlYtGiRUFN+vbbb+df/uVfjvuvJxER+fb55iMjk5OTj1pkHnlk5Lx585g2bVrg+LJly0hKSsLhcDBnzhySk5NZu3YtDocDl8vF//7v/wZ2Z1977bVccsklvPvuu8TExIR0R83jrpzvu+8+APLy8nj00Uc5++yzAfj888+54447Bu3X0tJCSkpKUIWUlJTg8XhYvXo1e/fuJS0tjblz5wKHV93Nzc3YbDYSEhJYsmQJZWVllJeX4/f7gxpn/fr1TJw4keTk5OO2q6iooKKiAoCD+799myJERExhFDad5eXlkZeXd8yf3XLLLdxyyy1HHf/zjdEAd999N3ffffdRx5966qlh1zikzzm/8847gWAGOOecc2hubh724N9UV1fH+vXrAx8E7+npobOzEzh8GeLITU8SExPp6OgYcHl8qPbv309JSQl1dXUnbOv1evF6vQDYokbuTX4REZETGVI4Hzp0iM8//5xzzjkHgM8++4z+/v5B29vt9qBvUmIYBtXV1SQkJAw4vmnTpgHPkI6IiDju2MfT1tbGBx98EFg179y5k5kzZ7J582YuvPDCkM4pIiIy0oZ0E5I77riDH/zgBxQVFfGLX/yCH/zgB4H3fo/F4/HQ29tLZWVl4FhTUxONjY2D9snMzGTVqlUYhgEwpJW51Wqlr69vKFMAICkpiY8//pj29nba29uJiYnhrbfe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SwspKSlB9SkpKcHj8bB69Wr27t1LWloac+fOBcDv99Pc3IzNZiMhIYElS5ZQVlZGeXk5fr8/qHG6urqIiooCICoqio8//viY7SoqKqioqADg4H7tWBQRkZPHNB+lqqurY/369axYsQKAnp4eOjs7AcjIyCAyMhKAxMREOjo6mDRpUljr8Xq9eL1eAGxR8WEdS0RE5JvCEs52u51169YF1ccwDKqrq0lISBhwfNOmTdhstsD3ERER9Pf3h1zbBRdcwEcffURUVBQfffQREyZMCPlcIiIi4RCW95w9Hg+9vb1UVlYGjjU1NdHY2Dhon8zMTFatWoVhGAA0NzefcByr1UpfX19QtS1cuJA1a9YAsGbNGq688sqg+ouIiIRbWMLZYrFQU1NDfX09cXFx2O12iouLiY6OHrRPUVERfX19OJ1OHA4HRUVFJxzH6/XidDqD2hBWWFhIfX098fHx1NfXU1hYOOS+IiIiJ4PFOLJUlUG53W58Pt9olyEiMqbod2fodIcwERERk1E4i4iImIzCWURExGQUziIiIiajcBYRETEZ7dYegjPOOINp06aNdhnD8sknn3D++eePdhnDojmYg+ZgDmNhDu3t7ezZs2e0yxiTTHP7TjObNm3amP84wLfhIw2agzloDubwbZiDDE6XtUVERExG4SwiImIyCuchOPJ0qrFMczAHzcEcNAcxO20IExERMRmtnEVERExG4SwiImIyCufjePHFF0lISGDKlCmUlZWNdjnHFRsbS1JSEi6XC7fbDcBnn33GvHnziI+PZ968eXz++eeB9g8++CBTpkwhISGBl156aVRqzs/PZ8KECTgcjsCxUGresmULSUlJTJkyhaVLl3Iy36k51hyKi4uZOHEiLpcLl8vFCy+8YOo57Nixgx/+8IdMnz4du93Oo48+Coyt12KwOYyl16Knp4e0tDSSk5Ox2+3cd999wNh6HWQEGXJM/f39xuTJk422tjajt7fXcDqdxrZt20a7rEF9//vfNz755JMBx5YtW2Y8+OCDhmEYxoMPPmgUFBQYhmEY27ZtM5xOp9HT02O8//77xuTJk43+/v6TXnNjY6OxZcsWw263D6vm1NRU4/XXXzcOHTpkzJ8/33jhhRdGdQ733Xef8atf/eqotmadw4cffmhs2bLFMAzD+PLLL434+Hhj27ZtY+q1GGwOY+m1OHTokLFv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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/adult_iid_10clients.csv\"\n",
    "partition_report(trainset.targets, iid_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "iid_part_df = pd.read_csv(csv_file,header=1)\n",
    "iid_part_df = iid_part_df.set_index('client')\n",
    "for col in col_names:\n",
    "    iid_part_df[col] = (iid_part_df[col] * iid_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "iid_part_df[col_names].plot.barh(stacked=True)  \n",
    "# plt.tight_layout()\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/adult_iid_10clients.png\", \n",
    "            dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.4 ('fedlab')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.4"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  },
  "vscode": {
   "interpreter": {
    "hash": "3ba7c791aa8d51007ff2c2ccf5bd0ffd8f40e8d3627fc5a38f863bdac0d8711e"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
